#include <iostream>
#include <fstream>
#include <iomanip> 
#include <string>
#include <list>

#include <readline/readline.h>
#include <readline/history.h>
#include <memory.h>

#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>

#include "XBaseClient.h"
#include "XToken.h"
#include "XShellCommand.h"
#include "XBaseServer.h"
#include "XUtility.h"

using namespace std;

#define STRING_IS(src, dst) ( src.length() == strlen( dst ) && strncasecmp( src.c_str(), dst, src.length() ) == 0 )
#define STRING_IS_NOT(src ,dst) (!(STRING_IS(src, dst)))
#define STRING_IS_NULL(str) (str.empty())
#define STRING_IS_NOT_NULL(str) (!(STRING_IS_NULL(str)))

static string g_ip_address;
static int g_port = 0;
static string g_tablet_id;

enum shell_type{
	SHELL_TYPE_CLIENT,
	SHELL_TYPE_ROOT,
	SHELL_TYPE_TABLET
};

shell_type g_shell_type = SHELL_TYPE_CLIENT;
#define SET_CLIENT_SHELL(a) ((a)=SHELL_TYPE_CLIENT)
#define SET_ROOT_SHELL(a) ((a)=SHELL_TYPE_ROOT)
#define SET_TABLET_SHELL(a) ((a)=SHELL_TYPE_TABLET)
#define IS_CLIENT_SHELL(a) ((a)==SHELL_TYPE_CLIENT)
#define IS_ROOT_SHELL(a) ((a)==SHELL_TYPE_ROOT)
#define IS_TABLET_SHELL(a) ((a)==SHELL_TYPE_TABLET)

XBaseClient* g_xbase;
XBaseServer* g_root;
XBaseServer* g_tablet;
extern int g_xbase_debug_verbose;

int run(const string& line, int* exit_flag=NULL);

struct COMMAND
{
  char* name;
};

static string NAMES[] = {
	"row", "ROW", 
	"where", "WHERE", 
	"create", "CREATE", 
	"drop", "DROP",
	"desc", "DESC",
	"end", "END",
	"from", "FROM",
	"insert", "INSERT",
	"into", "INTO",
	"scan", "SCAN",
	"select", "SELECT",
	"show", "SHOW",
	"table", "TABLE",
	"tables", "TABLES",
	"start", "START",
	"alter", "ALTER",
	"load", "LOAD",
	"command", "COMMAND",
	"family", "FAMILY",
	"group", "GROUP"
	};

COMMAND commands[256];

size_t split(std::vector<string>& result, const string & str, const char sp)
{
	size_t startPos = 0;
	size_t endPos = 0;
	while ( (endPos = str.find( sp, startPos )) != string::npos ) {
		if ( (endPos - startPos) >= 0 ) {
			result.push_back( str.substr( startPos, (endPos - startPos) ) );
		}

		startPos = endPos + 1;
	}
	if ( !str.empty() && startPos <= (str.length () - 1) ) {
		result.push_back( str.substr (startPos) );
	}

	return result.size();
}

void init_commands()
{
	unsigned int i = 0;
	for (; i < sizeof(NAMES)/sizeof(string); i++) {
		commands[i].name = (char*)(NAMES[i].c_str());
	}
	commands[i].name = NULL;
}

char *dupstr (char *s)
{
	char *r;
	r = (char *) malloc (strlen (s) + 1);
	strcpy (r, s);
	return (r);
}

char* next_command_generator (const char *text, int state)
{
	static int list_index, len;
	char *name;	

	if (!state) {
		list_index = 0;
		len = strlen (text);
	}

	while ((name = commands[list_index].name)) {
		list_index++;
		if (strncmp (name, text, len) == 0)
			return (dupstr (name));
	}

	return ((char *) NULL);
}

char **fileman_completion (const char *text, int start, int end)
{
	char **matches;
	matches = (char **) NULL;
	if (start == 0)
		matches = rl_completion_matches (text, next_command_generator);

	return (matches);
}

void welcome()
{
	//cout << "XQL, 0.2.0 version." << endl;
	//cout << "Copyright (c) 2008 by hulo, licensed to Thunder Networking Technologies." << endl;
	cout << "Welcome to the XbaseSQL monitor.  Commands end with \\n" << endl;
	cout << "Type 'help' for usage." << endl;
	cout << endl;
	
}
void usageHelp()
{
	cout << "HELP [command_name]" << endl;
}
void usageCreateTable()
{	
	cout << "CREATE TABLE table_name" << endl;
	cout << "    (column_definition [,column_definition] ...)" << endl;
	cout << "column_definition:" << endl;
	cout << "    family_name max_version max_ttl dead_time group_name" << endl;
	cout << "    | GROUP group_name compress_definition cache_rate block_size max_num_sstable max_size_sstable max_size_memtable" << endl;
	cout << "compress_definition:" << endl;
	cout << "    none | qkLZ | zlib | mLZO" << endl;
}
void usageDropTable()
{
	cout << "DROP TABLE table_name" << endl;
}
void usageDescTable()
{
	cout << "DESC table_name" << endl;
}
void usageAlterTable()
{
	cout << "ALTER TABLE table_name" << endl;
	cout << "    alter_specification [, alter_specification] ..." << endl;
	cout << "alter_specification::" << endl;
	cout << "    ADD FAMILY family_definition" << endl;
	cout << "    | ADD GROUP group_definition" << endl;
	cout << "    | DROP family_name" << endl;
	cout << "    | DROP group_name" << endl;
	cout << "family_definition:" << endl;
	cout << "    family_name max_version max_ttl dead_time [group_name]" << endl;
	cout << "group_definition:" << endl;
	cout << "    group_name compress_definition cache_rate block_size max_num_sstable max_size_sstable max_size_memtable" << endl;
	cout << "compress_definition:" << endl;
	cout << "    none | qkLZ | zlib | mLZO" << endl;
}
void usageInsertRecord()
{
	cout << "INSERT INTO table_name" << endl;
	cout << "    (column_definition [,column_definition] ...)" << endl;
	cout << "    VALUES (value [,value] ...)" << endl;
	cout << "    WHERE ROW=row" << endl;
	cout << "    [TIMESTAMP timestamp]" << endl;
	cout << "column_definition:" << endl;
	cout << "    family_name" << endl;
	cout << "    | family_name:label_name" << endl;
}
void usageSelectRecord()
{
	cout << "SELECT column_definition" << endl;
	cout << "    FROM table_name" << endl;
	cout << "    WHERE ROW=row" << endl;
	cout << "column_definition:" << endl;
	cout << "    * " << endl;
	cout << "    | family_name" << endl;
	cout << "    | family_name:label_name" << endl;
}
void usageScanRecord()
{
	cout << "SCAN column_definition [,column_definition] ..." << endl;
	cout << "    FROM table_name" << endl;
	cout << "    [START=start_row]" << endl;
	cout << "    [END=end_row]" << endl;
	cout << "    [OPTION=option]" << endl;
	cout << "    [VERSION=version]" << endl;
	cout << "    [LATESTTIMESTAMP=timestamp]" << endl;
	cout << "    [OLDESTTIMESTAMP=timestamp]" << endl;
	cout << "    [TTL=ttl]" << endl;
	cout << "    [NUM=num]" << endl;	
	cout << "column_definition:" << endl;
	cout << "    * " << endl;
	cout << "    | family_name" << endl;
	cout << "    | family_name:label_name" << endl;
}
void usageShowTables()
{
	cout << "SHOW TABLES" << endl;
	cout << "SHOW TABLE table_name" << endl;
}
void usageShowTablets()
{
	cout << "SHOW TABLETS" << endl;
}
void usageDeleteRecord()
{
	cout << "DELETE column_definition" << endl;
	cout << "    FROM table_name" << endl;
 	cout << "    WHERE ROW=row" << endl;
	cout << "    [TIMESTAMP timestamp]" << endl;
	cout << "column_definition:" << endl;
	cout << "    * " << endl;
	cout << "    | family_name" << endl;
	cout << "    | family_name:label_name" << endl;
}
void usageLoadCmd()
{
	cout << "LOAD COMMAND FROM file_name" << endl;
}
void help()
{
	usageCreateTable();
	cout << endl;

	usageDropTable();
	cout << endl;
	
	usageDescTable();
	cout << endl;

	usageAlterTable();
	cout << endl;

	usageInsertRecord();
	cout << endl;

	usageSelectRecord();
	cout << endl;

	usageScanRecord();
	cout << endl;

	usageDeleteRecord();
	cout << endl;

	usageShowTables();
	cout << endl;

	usageShowTablets();
	cout << endl;

	usageLoadCmd();
	cout << endl;

	usageHelp();
	cout << endl;
}
int exit()
{
	cout << "Bye" << endl;
	return 0;
}

int help(XToken& word_parse)
{
	//cout << __FUNCTION__ << endl;

	string cmd_name = word_parse.getNextWord( " " );
	if (cmd_name.empty()) {
		help();
		return 0;
	}

	if (cmd_name == "create" || cmd_name == "CREATE") {
		usageCreateTable();
		cout << endl;
	}
	else if (cmd_name == "drop" || cmd_name == "DROP") {
		usageDropTable();
		cout << endl;
	}
	else if (cmd_name == "desc" || cmd_name == "DESC") {
		usageDescTable();
		cout << endl;
	}
	else if (cmd_name == "alter" || cmd_name == "ALTER") {
		usageAlterTable();
		cout << endl;
	}
	else if (cmd_name == "insert" || cmd_name == "INSERT") {
		usageInsertRecord();
		cout << endl;
	}
	else if (cmd_name == "select" || cmd_name == "SELECT") {
		usageSelectRecord();
		cout << endl;
	}
	else if (cmd_name == "scan" || cmd_name == "SCAN") {
		usageScanRecord();
		cout << endl;
	}
	else if (cmd_name == "delete" || cmd_name == "DELETE") {
		usageDeleteRecord();
		cout << endl;
	}
	else if (cmd_name == "show" || cmd_name == "SHOW") {
		usageShowTables();
		cout << endl;
	}
	else if (cmd_name == "load" || cmd_name == "LOAD") {
		usageLoadCmd();
		cout << endl;
	}
	else if (cmd_name == "help" || cmd_name == "HELP") {
		usageHelp();
		cout << endl;
	}	
	else {
		help();
	}

	return 0;
}
int createTable(string& cmd_name, XToken& word_parse)
{
	XShellCommandCreate cmd;
	if ( !cmd.parse( word_parse.toString() ) ) {
		cout << cmd.errormsg() << endl;
		return -1;
	}
	
	uint32_t time_begin = get_cur_millseconds();
	g_xbase->execute( &cmd );
	uint32_t time_used = get_cur_millseconds() - time_begin;
	if (cmd.fail()) {
		cout << cmd.errorstr() << endl;
		return -2;
	}
	else {
		cout << endl << "Query OK, " << cmd.affected_rows() << " row(s) affected (" << fixed << setprecision(2) << (float)time_used/1000 << " sec)" << endl;
		return 0;
	}
}
int dropTable(string& cmd_name, XToken& word_parse)
{
	XShellCommandDrop cmd;
	if ( !cmd.parse( word_parse.toString() ) ) {
		cout << cmd.errormsg() << endl;
		return -1;
	}

	uint32_t time_begin = get_cur_millseconds();
	g_xbase->execute( &cmd );
	uint32_t time_used = get_cur_millseconds() - time_begin;
	if (cmd.fail()) {
		cout << cmd.errorstr() << endl;
		return -2;
	}
	else {
		cout << endl << "Query OK, " << cmd.affected_rows() << " rows affected (" << fixed << setprecision(2) << (float)time_used/1000 << " sec)" << endl;
		return 0;
	}	
}
int descTable(string& cmd_name, XToken& word_parse)
{
	XShellCommandDesc cmd;
	if ( !cmd.parse( word_parse.toString() ) ) {
		cout << cmd.errormsg() << endl;
		return -1;
	}

	uint32_t time_begin = get_cur_millseconds();
	g_xbase->execute(&cmd);
	uint32_t time_used = get_cur_millseconds() - time_begin;
	if (cmd.fail()) {
		cout << cmd.errorstr() << endl;
		return -2;
	}
	else {
		uint32_t max_family_name_length = 0;
		uint32_t max_group_name_length = 0;
		for (XBaseFamily::iterator it = cmd.family().begin(); it != cmd.family().end(); ++it) {
			if (it->name().length() > max_family_name_length) {
				max_family_name_length = it->name().length();
			}
		}
		for (XBaseGroup::iterator it = cmd.group().begin(); it != cmd.group().end(); ++it) {
			if (it->name().length() > max_group_name_length) {
				max_group_name_length = it->name().length();
			}
		}

		cout << setiosflags(ios::left) << setw(max_family_name_length) << "Family" << "\t";
		cout << setiosflags(ios::left) << setw(max_group_name_length) << "Group" << "\t";
		cout << setiosflags(ios::left) << setw(15) << "MaxVersion" << "\t";
		cout << setiosflags(ios::left) << setw(10) << "MaxTTL" << "\t";
		cout << setiosflags(ios::left) << setw(10) << "DeadTime" << "\n";		
		for (XBaseFamily::iterator it = cmd.family().begin(); it != cmd.family().end(); ++it) {
			cout << setiosflags(ios::left) << setw(max_family_name_length) << it->name() << "\t";
			cout << setiosflags(ios::left) << setw(max_group_name_length) << it->group_name() << "\t";
			cout << setiosflags(ios::left) << setw(15) << it->max_num_version() << "\t";
			cout << setiosflags(ios::left) << setw(10) << it->max_ttl() << "\t";
			cout << setiosflags(ios::left) << setw(10) << it->dead_time() << "\n";			
		}

		cout << setiosflags(ios::left) << setw(max_group_name_length) << "Group" << "\t";
		cout << setiosflags(ios::left) << setw(15) << "CompressMethod" << "\t";
		cout << setiosflags(ios::left) << setw(10) << "CacheRate" << "\t";
		cout << setiosflags(ios::left) << setw(10) << "BlockSize" << "\t";
		cout << setiosflags(ios::left) << setw(15) << "MaxNumSSTable" << "\t";
		cout << setiosflags(ios::left) << setw(15) << "MaxSizeSSTable" << "\t";
		cout << setiosflags(ios::left) << setw(15) << "MaxSizeMemTable" << "\n";
		for (XBaseGroup::iterator it = cmd.group().begin(); it != cmd.group().end(); ++it) {
			cout << setiosflags(ios::left) << setw(max_group_name_length) << it->name() << "\t";
			cout << setiosflags(ios::left) << setw(15) << it->compress_method_to_string() << "\t";
			cout << setiosflags(ios::left) << setw(10) << it->cache_rate() << "\t";
			cout << setiosflags(ios::left) << setw(10) << it->block_size() << "\t";
			cout << setiosflags(ios::left) << setw(15) << it->max_num_sstable() << "\t";			
			cout << setiosflags(ios::left) << setw(15) << it->max_size_sstable() << "\t";			
			cout << setiosflags(ios::left) << setw(15) << it->max_size_memtable() << "\n";			
		}

		cout << endl << cmd.affected_rows() << " row(s) in set (" << fixed << setprecision(2) << (float)time_used/1000 << " sec)" << endl;
		return 0;
	}
}
int alterTable(string& cmd_name, XToken& word_parse)
{
	XShellCommandAlter cmd;
	if ( !cmd.parse( word_parse.toString() ) ) {
		cout << cmd.errormsg() << endl;
		return -1;
	}
	
	uint32_t time_begin = get_cur_millseconds();
	g_xbase->execute(&cmd);
	uint32_t time_used = get_cur_millseconds() - time_begin;
	if (cmd.fail()) {
		cout << cmd.errorstr() << endl;
		return -2;
	}
	else {
		cout << endl << cmd.affected_rows() << " row(s) in set(" << fixed << setprecision(2) << (float)time_used/1000 << " sec)" << endl;
		return 0;
	}	
}
int insertRecord(string& cmd_name, XToken& word_parse)
{
	XShellCommandInsert cmd;
	if ( !cmd.parse( word_parse.toString() ) ) {
		cout << cmd.errormsg() << endl;
		return -1;
	}

	if ( IS_ROOT_SHELL( g_shell_type ) ) {
		cmd.tablet_name() = ROOT_META_TABLET_NAME;
	} else if ( IS_TABLET_SHELL( g_shell_type ) ) {
		cmd.tablet_name() = g_tablet_id;
	} else {
		if ( STRING_IS( cmd.table_name(), "root" ) ) {
			cmd.tablet_name() = ROOT_META_TABLET_NAME;
		}
	}

	uint32_t time_begin = get_cur_millseconds();
	if ( IS_ROOT_SHELL( g_shell_type ) ) {
		g_xbase->execute( &cmd, g_root );
	} else if ( IS_TABLET_SHELL( g_shell_type ) ) {
		g_xbase->execute( &cmd, g_tablet );
	} else {
		if ( STRING_IS( cmd.tablet_name(), ROOT_META_TABLET_NAME ) ) {
			g_xbase->execute( &cmd, g_root );
		} else {
			g_xbase->execute(&cmd);
		}
	}
	uint32_t time_used = get_cur_millseconds() - time_begin;
	if (cmd.fail()) {
		cout << cmd.errorstr() << endl;
		return -2;
	}
	else {
		cout << endl << "Query OK, " << cmd.affected_rows() << " row(s) affected (" << fixed << setprecision(2) << (float)time_used/1000 << " sec)" << endl;
		return 0;
	}
}
int selectRecord(string& cmd_name, XToken& word_parse)
{
	XShellCommandSelect cmd;
	if ( !cmd.parse( word_parse.toString() ) ) {
		cout << cmd.errormsg() << endl;
		return -1;
	}

	if ( IS_ROOT_SHELL( g_shell_type ) ) {
		cmd.tablet_name() = ROOT_META_TABLET_NAME;
	} else if ( IS_TABLET_SHELL( g_shell_type ) ) {
		cmd.tablet_name() = g_tablet_id;
	} else {
		if ( STRING_IS( cmd.table_name(), "root" ) ) {
			cmd.tablet_name() = ROOT_META_TABLET_NAME;
		}
	}

	uint32_t time_begin = get_cur_millseconds();
	if ( IS_ROOT_SHELL( g_shell_type ) ) {
		g_xbase->execute( &cmd, g_root );
	} else if ( IS_TABLET_SHELL( g_shell_type ) ) {
		g_xbase->execute( &cmd, g_tablet );
	} else {
		if ( STRING_IS( cmd.tablet_name(), ROOT_META_TABLET_NAME ) ) {
			g_xbase->execute( &cmd, g_root );
		} else {
			g_xbase->execute(&cmd);
		}
	}
	uint32_t time_used = get_cur_millseconds() - time_begin;
	if (cmd.fail()) {
		cout << cmd.errorstr() << endl;
		return -2;
	}
	else {
		
		int width = 12;
		int row = 1;
		for (XBaseRecord::iterator it = cmd.record().begin(); it != cmd.record().end(); ++it) {

			cout << "*************************** " << (row++) << ". row ***************************" << endl;
			if ( cmd.mUnixtime ) {
				cout << setiosflags(ios::right) << setw(width) << "Timestamp: "
					<< it->timestamp() << "\n";							
			} else {				
				cout << setiosflags(ios::right) << setw(width) << "Timestamp: "
					<< XUtility::time_2_string( it->timestamp() ) << "\n";	
			}
			if ( cmd.mISRowShow ) {
				if ( cmd.mISRowHex ) {
					cout << setiosflags(ios::right) << setw(width) << "HEX(Row): "
						<< XUtility::str_2_hex( it->row() ) << "\n";
				} else {
					cout << setiosflags(ios::right) << setw(width) << "Row: "
						<< it->row() << "\n";
				}
			}
			if ( cmd.mISColumnShow ) {
				if ( cmd.mISColumnHex ) {
					if ( it->column().find(':') == string::npos ) {
						cout	<< setiosflags(ios::right) << setw(width) << "HEX(Column): "
							<< it->column() << "\n";
					} else {
						cout	<< setiosflags(ios::right) << setw(width) << "HEX(Column): "
							<< it->column().substr( 0, it->column().find(':') + 1 ) << XUtility::str_2_hex( it->column().substr( it->column().find(':') + 1 ) ) << "\n";
					}
				} else {
					cout	<< setiosflags(ios::right) << setw(width) << "Column: "
						<< it->column() << "\n";
				}
			}
			if ( cmd.mISValueShow ) {
				if ( cmd.mISValueHex ) {
					cout	<< setiosflags(ios::right) << setw(width) << "HEX(Value): "
						<< XUtility::str_2_hex( it->value() ) << endl;
				} else {
					cout	<< setiosflags(ios::right) << setw(width) << "Value: "
						<< it->value() << endl;
				}
			}
		}		
	
		cout << endl << "Query OK, " << cmd.affected_rows() << " row(s) affected (" << fixed << setprecision(2) << (float)time_used/1000 << " sec)" << endl;
		return 0;
	}
}
int scanRecord(string& cmd_name, XToken& word_parse)
{
	XShellCommandScan cmd;
	if ( !cmd.parse( word_parse.toString() ) ) {
		cout << cmd.errormsg() << endl;
		return -1;
	}

	if ( IS_ROOT_SHELL( g_shell_type ) ) {
		cmd.tablet_name() = ROOT_META_TABLET_NAME;
	} else if ( IS_TABLET_SHELL( g_shell_type ) ) {
		cmd.tablet_name() = g_tablet_id;
	} else {
		if ( STRING_IS( cmd.table_name(), "root" ) ) {
			cmd.tablet_name() = ROOT_META_TABLET_NAME;
		}
	}

	if ( (cmd.column() == "root:" || cmd.column() == "ROOT:") && (cmd.table_name() == "master" || cmd.table_name() == "MASTER") ) {
		uint32_t time_begin = get_cur_millseconds();
		XBaseServer* rootTablet = g_xbase->load_root_tablet();
		uint32_t time_used = get_cur_millseconds() - time_begin;
		if ( !rootTablet ) {
			cmd.errorno( g_xbase->errorno() );
			cmd.errorstr( g_xbase->errorstr() );
			cout << cmd.errorstr() << endl;
			return -2;
		} else {
			cout << rootTablet->tabletAddress().to_string() << endl;
			cout << endl << "Query OK, (" << fixed << setprecision(2) << (float)time_used/1000 << " sec)" << endl;			
			return 0;
		}
	} else {	
		uint32_t time_begin = get_cur_millseconds();
		if ( IS_ROOT_SHELL( g_shell_type ) ) {
			g_xbase->scan_open( &cmd, g_root );
		} else if ( IS_TABLET_SHELL( g_shell_type ) ) {
			g_xbase->scan_open( &cmd, g_tablet );
		} else {
			if ( STRING_IS( cmd.tablet_name(), ROOT_META_TABLET_NAME ) ) {
				g_xbase->scan_open( &cmd, g_root );
			} else {
				g_xbase->scan_open(&cmd);
			}
		}
		if ( cmd.eof() ) {
			uint32_t time_used = get_cur_millseconds() - time_begin;
			cout << endl << "Query OK, " << cmd.affected_rows() << " row(s) affected (" << fixed << setprecision(2) << (float)time_used/1000 << " sec)" << endl;
			return 0;
		}
		if (cmd.fail()) {
			cout << cmd.errorstr() << endl;	
			return -2;
		}

		int width = 12;
		int row = 1;
		vector<XBaseRecord> record_list;
		while (true) {
			// next a scaner
			if ( IS_ROOT_SHELL( g_shell_type ) ) {
				g_xbase->scan_next( &cmd, g_root );
			} else if ( IS_TABLET_SHELL( g_shell_type ) ) {
				g_xbase->scan_next( &cmd, g_tablet );
			} else {
				if ( STRING_IS( cmd.tablet_name(), ROOT_META_TABLET_NAME ) ) {
					g_xbase->scan_next( &cmd, g_root );
				} else {
					g_xbase->scan_next(&cmd);
				}
			}
			if ( cmd.eof() && cmd.record().empty() ) {
				break;
			}
			if (cmd.fail()) {
				cout << "error:" << cmd.errorstr() << endl;
				g_xbase->scan_close(&cmd);
				return -2;
				break;
			}
			else {			
				XBaseRecord& record = cmd.record();
				if (!record.empty()) {					
					for (XBaseRecord::iterator it = record.begin(); it != record.end(); ++it) {

						cout << "*************************** " << (row++) << ". row ***************************" << endl;
						if ( cmd.mUnixtime ) {
							cout << setiosflags(ios::right) << setw(width) << "Timestamp: "
								<< it->timestamp() << "\n";							
						} else {				
							cout << setiosflags(ios::right) << setw(width) << "Timestamp: "
								<< XUtility::time_2_string( it->timestamp() ) << "\n";	
						}
						if ( cmd.mISRowShow  ) {
							if ( cmd.mISRowHex ) {
								cout << setiosflags(ios::right) << setw(width) << "HEX(Row): "
									<< XUtility::str_2_hex( it->row() ) << "\n";
							} else {
								cout << setiosflags(ios::right) << setw(width) << "Row: "
									<< it->row() << "\n";
							}
						}
						if ( cmd.mISColumnShow ) {
							if ( cmd.mISColumnHex ) {
								if ( it->column().find(':') == string::npos ) {
									cout	<< setiosflags(ios::right) << setw(width) << "HEX(Column): "
										<< it->column() << "\n";
								} else {
									cout	<< setiosflags(ios::right) << setw(width) << "HEX(Column): "
										<< it->column().substr( 0, it->column().find(':') + 1 ) << XUtility::str_2_hex( it->column().substr( it->column().find(':') + 1 ) ) << "\n";
								}
							} else {
								cout	<< setiosflags(ios::right) << setw(width) << "Column: "
									<< it->column() << "\n";
							}
						}
						if ( cmd.mISValueShow ) {
							if ( IS_ROOT_SHELL( g_shell_type ) || STRING_IS( cmd.table_name(), "root" ) ) {
								if ( it->column().find( ROOT_META_COLUMN_NAME[RMC_SERVER_ADDRESS] ) != string::npos ) {
									if ( it->value().empty() ) {
										cout	<< setiosflags(ios::right) << setw(width) << "Value: "
											<< endl;
									} else {
										XStringStream decoder(it->value().c_str(), it->value().length(), XStringStream::NODELETE, XStringStream::BIGENDIAN);
										ServerAddress tabletAddress;
										decoder >> tabletAddress;
										cout	<< setiosflags(ios::right) << setw(width) << "Value: "
											<< tabletAddress.to_string() << endl;
									}
								} else if ( it->column().find( ROOT_META_COLUMN_NAME[RMC_SERVER_START_CODE] ) != string::npos ) {
									if ( it->value().empty() ) {
										cout	<< setiosflags(ios::right) << setw(width) << "Value: "
											<< endl;
									} else {
										XStringStream decoder(it->value().c_str(), it->value().length(), XStringStream::NODELETE, XStringStream::BIGENDIAN);
										uint64_t startCode;
										decoder >> startCode;
										cout	<< setiosflags(ios::right) << setw(width) << "Value: "
											<< startCode << endl;
									}
								} else if ( it->column().find( ROOT_META_COLUMN_NAME[RMC_TABLET_INFO] ) != string::npos ) {
									if ( it->value().empty() ) {
										cout	<< setiosflags(ios::right) << setw(width) << "Value: "
											<< endl;
									} else {
										XStringStream decoder(it->value().c_str(), it->value().length(), XStringStream::NODELETE, XStringStream::BIGENDIAN);
										TabletInfo tabletInfo;
										decoder >> tabletInfo;
										cout	<< setiosflags(ios::right) << setw(width) << "Value: "
											<< tabletInfo.to_string() << endl;
									}
								} else if ( it->column().find( ROOT_META_COLUMN_NAME[RMC_SPLITA] ) != string::npos ) {
									if ( it->value().empty() ) {
										cout	<< setiosflags(ios::right) << setw(width) << "Value: "
											<< endl;
									} else {
										XStringStream decoder(it->value().c_str(), it->value().length(), XStringStream::NODELETE, XStringStream::BIGENDIAN);
										string value;
										decoder >> value;
										cout	<< setiosflags(ios::right) << setw(width) << "Value: "
											<< value << endl;
									}
								} else if ( it->column().find( ROOT_META_COLUMN_NAME[RMC_SPLITB] ) != string::npos ) {
									if ( it->value().empty() ) {
										cout	<< setiosflags(ios::right) << setw(width) << "Value: "
											<< endl;
									} else {
										XStringStream decoder(it->value().c_str(), it->value().length(), XStringStream::NODELETE, XStringStream::BIGENDIAN);
										string value;
										decoder >> value;
										cout	<< setiosflags(ios::right) << setw(width) << "Value: "
											<< value << endl;
									}
								} else {
									if ( cmd.mISValueHex ) {
										cout	<< setiosflags(ios::right) << setw(width) << "HEX(Value): "
											<< XUtility::str_2_hex(it->value()) << endl;
									} else {
										cout	<< setiosflags(ios::right) << setw(width) << "Value: "
											<< it->value() << endl;
									}
								}
							} else {
								if ( cmd.mISValueHex ) {
									cout	<< setiosflags(ios::right) << setw(width) << "HEX(Value): "
										<< XUtility::str_2_hex(it->value()) << endl;
								} else {
									cout	<< setiosflags(ios::right) << setw(width) << "Value: "
										<< it->value() << endl;
								}
							}
						}						
					}
				}			
			}

			if (cmd.eof()) {
				break;
			}
		}

		if ( IS_ROOT_SHELL( g_shell_type ) ) {
			g_xbase->scan_close( &cmd, g_root );
		} else if ( IS_TABLET_SHELL( g_shell_type ) ) {
			g_xbase->scan_close( &cmd, g_tablet );
		} else {
			if ( STRING_IS( cmd.tablet_name(), ROOT_META_TABLET_NAME ) ) {
				g_xbase->scan_close( &cmd, g_root );
			} else {
				g_xbase->scan_close(&cmd);
			}
		}
		uint32_t time_used = get_cur_millseconds() - time_begin;	
		if (cmd.fail()) {
			cout << "error:" << cmd.errorstr() << endl;
			return -2;
		}
		else {
			cout << endl << "Query OK, " << cmd.affected_rows() << " row(s) affected (" << fixed << setprecision(2) << (float)time_used/1000 << " sec)" << endl;
			return 0;
		}			
	}
}
int deleteRecord(string& cmd_name, XToken& word_parse)
{
	XShellCommandDelete cmd;
	if ( !cmd.parse( word_parse.toString() ) ) {
		cout << cmd.errormsg() << endl;
		return -1;
	}

	if ( IS_ROOT_SHELL( g_shell_type ) ) {
		cmd.tablet_name() = ROOT_META_TABLET_NAME;
	} else if ( IS_TABLET_SHELL( g_shell_type ) ) {
		cmd.tablet_name() = g_tablet_id;
	} else {
		if ( STRING_IS( cmd.table_name(), "root" ) ) {
			cmd.tablet_name() = ROOT_META_TABLET_NAME;
		}
	}

	uint32_t time_begin = get_cur_millseconds();
	if ( IS_ROOT_SHELL( g_shell_type ) ) {
		g_xbase->execute( &cmd, g_root );
	} else if ( IS_TABLET_SHELL( g_shell_type ) ) {
		g_xbase->execute( &cmd, g_tablet );
	} else {
		if ( STRING_IS( cmd.tablet_name(), ROOT_META_TABLET_NAME ) ) {
			g_xbase->execute( &cmd, g_root );
		} else {
			g_xbase->execute(&cmd);
		}
	}
	uint32_t time_used = get_cur_millseconds() - time_begin;

	if (cmd.fail()) {
		cout << cmd.errorstr() << endl;
		return -2;
	}
	else {
		cout << endl << "Query OK, " << cmd.affected_rows() << " row(s) affected (" << fixed << setprecision(2) << (float)time_used/1000 << " sec)" << endl;
		return 0;
	}
}
int showTables(string& cmd_name, XToken& word_parse)
{
	XShellCommandShow cmd;

	string expectString;	

	if ( !cmd.parse( word_parse.toString() ) ) {
		cout << cmd.errormsg() << endl;
		return -1;
	}

	if ( cmd.table_name().empty() ) {
		if ( STRING_IS( cmd.mTable, "tables" ) ) {
			if ( IS_TABLET_SHELL( g_shell_type ) ) {
				cout << "XBaseShell run as tablet!!!" << endl;
				return 0;
			}
			
			XBaseCommandShow show;
			show.set_table_name( cmd.table_name() );
			uint32_t time_begin = get_cur_millseconds();
			g_xbase->execute( &show );
			uint32_t time_used = get_cur_millseconds() - time_begin;
			if (show.fail()) {
				cout << show.errorstr() << endl;
				return -2;
			}
			else {
				for(XBaseCommandShow::iterator it = show.begin(); it != show.end(); ++it) {
					cout << *it << endl;
				}
				cout << endl << show.size() << " row(s) in set(" << fixed << setprecision(2) << (float)time_used/1000 << " sec)" << endl;
				return 0;
			}
		} else if ( IS_TABLET_SHELL( g_shell_type ) ) {
			XBaseCommandList list;
			uint32_t time_begin = get_cur_millseconds();
			g_xbase->execute( &list, g_tablet );
			uint32_t time_used = get_cur_millseconds() - time_begin;
			if (list.fail()) {
				cout << list.errorstr() << endl;
				return -2;
			}
			else {
				for(XBaseCommandList::iterator it = list.begin(); it != list.end(); ++it) {
					cout << it->to_string() << endl;
				}
				cout << endl << list.size() << " row(s) in set(" << fixed << setprecision(2) << (float)time_used/1000 << " sec)" << endl;
				return 0;
			}
		} else {
			cout << "XBaseShell not run as tablet!!!" << endl;
			return 0;
		}
	}

	else if ( STRING_IS( cmd.table_name(), "root" )  ) {
		uint32_t time_begin = get_cur_millseconds();
		XBaseServer* rootTablet = g_xbase->load_root_tablet();
		uint32_t time_used = get_cur_millseconds() - time_begin;
		if ( !rootTablet ) {
			cmd.errorno( g_xbase->errorno() );
			cmd.errorstr( g_xbase->errorstr() );
			cout << cmd.errorstr() << endl;
			return -2;
		} else {
			cout << rootTablet->tabletAddress().to_string() << endl;
			cout << endl << "Query OK, (" << fixed << setprecision(2) << (float)time_used/1000 << " sec)" << endl;			
			return 0;
		}
	} else {
		XBaseServer* root_tablet = g_xbase->load_root_tablet();	
		if (root_tablet == 0) {
			cmd.errorno( g_xbase->errorno() );
			cmd.errorstr( g_xbase->errorstr() );
			cout << cmd.errorstr() << endl;
			return -2;
		}
		
		int affected_rows = 0;
		uint32_t time_begin = get_cur_millseconds();
		g_xbase->scan_open( &cmd, root_tablet );
		if ( cmd.eof() ) {
			uint32_t time_used = get_cur_millseconds() - time_begin;
			cout << endl << affected_rows << " row(s) in set (" << fixed << setprecision(2) << (float)time_used/1000 << " sec)" << endl;
			return 0;
		}
		if (cmd.fail()) {
			cout << cmd.errorstr() << endl;		
			return -2;
		}

		int row = 1;
		map<string, bool> table_name_list;	
		while (true) {
			g_xbase->scan_next( &cmd, root_tablet );
			if ( cmd.eof() && cmd.record().empty() ) {
				break;
			}
			if (cmd.fail()) {
				cout << "error:" << cmd.errorstr() << endl;
				g_xbase->scan_close( &cmd, root_tablet );
				return -2;
				break;
			}
			else {
				bool has_server_address = false;
				bool has_tablet_info = false;
				XBaseRecord& record = cmd.record();
				for (XBaseRecord::iterator it = record.begin(); it != record.end(); ++it) {				
					XStringStream decoder(it->value().c_str(), it->value().length(), XStringStream::NODELETE, XStringStream::BIGENDIAN);
					if ( it->column().find( ROOT_META_COLUMN_NAME[RMC_SERVER_ADDRESS] ) != string::npos ) {
						if ( has_server_address == false && has_tablet_info == false ) {
							cout << "*************************** " << (row++) << ". row ***************************" << endl;
						}
						ServerAddress serverAddress;
						decoder >> serverAddress;				
						cout << serverAddress.to_string() << endl;
						has_server_address = true;
					}
					else if ( it->column().find( ROOT_META_COLUMN_NAME[RMC_TABLET_INFO] ) != string::npos ) {
						if ( has_server_address == false && has_tablet_info == false ) {
							cout << "*************************** " << (row++) << ". row ***************************" << endl;
						}
						TabletInfo tabletInfo;
						decoder >> tabletInfo;				
						//cout << tabletInfo.to_string() << endl;
						cout << tabletInfo.tablet_name() << endl;
						has_tablet_info = true;
					}
					if ( has_tablet_info && has_server_address ) {
						has_server_address = false;
						has_tablet_info = false;
						++affected_rows;
					}					
				}
			}

			if (cmd.eof()) {
				break;
			}
		}

		g_xbase->scan_close( &cmd, root_tablet );
		uint32_t time_used = get_cur_millseconds() - time_begin;
		if (cmd.fail()) {
			cout << "error:" << cmd.errorstr() << endl;
			return -2;
		}
		else {
			cout << endl << affected_rows << " row(s) in set (" << fixed << setprecision(2) << (float)time_used/1000 << " sec)" << endl;
			return 0;
		}
	}
}
int loadCommand(string& cmd_name, XToken& word_parse)
{
	string command = word_parse.getNextWord( " " );
	if (command != "command" && command != "COMMAND") {
		cout << "You have an error in your XBASE SQL syntax near \'load\'" << endl;
		return -1;
	}

	string from = word_parse.getNextWord( " " );
	if (from != "from" && from != "FROM") {
		cout << "You have an error in your XBASE SQL syntax near \'" << command << "\'" << endl;
		return -1;
	}

	string fileName = word_parse.getNextWord( " " );
	if ( fileName.empty() ) {
		cout << "You have an error in your XBASE SQL syntax near \'" << from << "\'" << endl;
		return -1;
	}

	ifstream in( fileName.c_str() );
	if ( in.fail() ) {
		cout << "open file " << fileName << " fail: " << strerror( errno ) << endl;
		return -2;
	}

	int taskCount = 0;
	string line;
	uint32_t time_begin = get_cur_millseconds();
	while ( getline( in, line ) ) {
		if ( !line.empty() ) {
			++taskCount;
			cout << "*************************** " << taskCount << ". command ***************************" << endl;
			cout << "xbasesql> " << line << endl;
			run( line );
		}
	}

	uint32_t time_used = get_cur_millseconds() - time_begin;
	cout << endl << "Total " << taskCount << " task(s) (" << fixed << setprecision(2) << (float)time_used/1000 << " sec)" << endl;
	return 0;
}

void getline(string& line)
{
	getline(cin, line);	
}

int run(const string& line, int* exit_flag)
{
	if ( exit_flag ) {
		*exit_flag = 0;
	}
	int res = 0;
	do {	
		XToken word_parse( line );
		string cmd_name = word_parse.getNextWord( " " );
		if (cmd_name.empty()) {
			res = true;
		}
		else if (cmd_name == "create" || cmd_name == "CREATE") {
			res = createTable(cmd_name, word_parse);
		}
		else if (cmd_name == "drop" || cmd_name == "DROP") {
			res = dropTable(cmd_name, word_parse);
		}
		else if (cmd_name == "desc" || cmd_name == "DESC") {
			res = descTable(cmd_name, word_parse);
		}
		else if (cmd_name == "alter" || cmd_name == "ALTER") {
			res = alterTable(cmd_name, word_parse);
		}
		else if (cmd_name == "insert" || cmd_name == "INSERT") {
			res = insertRecord(cmd_name, word_parse);
		}
		else if (cmd_name == "select" || cmd_name == "SELECT") {
			res = selectRecord(cmd_name, word_parse);
		}
		else if (cmd_name == "scan" || cmd_name == "SCAN") {
			res = scanRecord(cmd_name, word_parse);
		}
		else if (cmd_name == "delete" || cmd_name == "DELETE") {
			res = deleteRecord(cmd_name, word_parse);
		}
		else if (cmd_name == "show" || cmd_name == "SHOW") {
			res = showTables(cmd_name, word_parse);
		}
		else if (cmd_name == "load" || cmd_name == "LOAD") {
			res = loadCommand(cmd_name, word_parse);
		}
		else if (cmd_name == "help" || cmd_name == "HELP") {
			res = help(word_parse);
		}
		else if (cmd_name == "exit" || cmd_name == "EXIT" || cmd_name == "quit" || cmd_name == "QUIT") {
			res = exit();
			if ( exit_flag ) {
				*exit_flag = 1;
			}
		}
		else {
			cout << "You have an error in your XBASE SQL syntax near \'" << cmd_name << "\'" << endl;
			res = -1;
		}
		
	} while ( false );

	return res;
}
void loadHistoryCommand( const string& filename, list< string >& historyCmds )
{
	//add_history( ptr );
	FILE* fp = fopen( filename.c_str(), "r" );
	if ( !fp ) {
		fp = fopen( filename.c_str(), "w" );
		fclose( fp );
		fp = fopen( filename.c_str(), "r" );
		if ( !fp ) {			
			cout << "open file " << filename << " fail: " << strerror( errno ) << endl;
			return;
		}
	}

	char line[2048];
	while ( fgets( line, sizeof(line), fp ) ) {
		if ( line[ strlen( line ) -1 ] == '\n' ) {
			line[ strlen( line ) - 1 ] = '\0';
		}
		add_history( line );
		historyCmds.push_back( line );
	}

	fclose( fp );
}

void writeHistoryCommand( const string& filename, list< string>& historyCmds )
{
	FILE* fp = fopen( filename.c_str(), "w" );
	if ( !fp ) {
		cout << "open file " << filename << " fail: " << strerror( errno ) << endl;
	}

	for ( list< string >::iterator it=historyCmds.begin(); it!=historyCmds.end(); ++it ) {
		fputs( it->c_str(), fp );
		fputs( "\n", fp );
	}

	fclose( fp );
}
int main(int argc, char* argv[])
{
	if ( argc <= 1 ) {
		cout << "Usage: " << argv[0] << " -h<ip> -p<port> [-i<tablet_id>]" << endl;
		cout << "Example: " << argv[0] << " -h127.0.0.1 -p20081 [-isd_server_res,,1208485730497260]" << endl;

		exit( EXIT_FAILURE );
	}

	string cmdLine;
	string scriptFile;

	extern char *optarg;
	extern int optopt;

	char c;
	 while ( (c = getopt( argc, argv, ":h:i:p:e:s:t:g" ) ) != -1 ) {
	 	switch( c ) {
			case 'h':
				g_ip_address = optarg; 
				break;
			case 'p':           
				g_port = atoi ( optarg );
				break;	
			case 'e':           
				cmdLine =  optarg;
				break;
			case 's':           
				scriptFile =  optarg;
				break;
			case 'g':           
				g_xbase_debug_verbose = 1;
				break;
			case 't':
				if ( strcmp( optarg, "root" ) == 0 ) {
					SET_ROOT_SHELL( g_shell_type );
				} else if ( strcmp( optarg, "tablet" ) == 0) {
					SET_TABLET_SHELL( g_shell_type );
				} else {
					fprintf( stderr, "Invalid arg for option -t, use default value\n" );
					SET_CLIENT_SHELL( g_shell_type );
				}
				break;
			case 'i':
				g_tablet_id = g_tablet_id = optarg;
				SET_TABLET_SHELL( g_shell_type );
				break;
			case ':':       /* without operand */
				fprintf(stderr, "Option -%c requires an operand\n", optopt);    
				exit( EXIT_FAILURE );
				break;
			case '?':
				fprintf(stderr, "Unrecognized option: -%c\n", optopt);
				exit( EXIT_FAILURE );
				break;
		}
	}	

	XBaseClient::turn_on_log();
	XBaseClient cn(g_ip_address.c_str(), g_port);
	cn.set_recv_timeout( 3600*1000 );
	cn.set_send_timeout( 3600*1000 );
	g_xbase = &cn;

	XBaseServer rootTablet;
	//if ( IS_ROOT_SHELL( g_shell_type ) ) 
	{
		if ( g_ip_address.empty() || g_port == 0 ) {
		 	cout << "Usage: " << argv[0] << " -h<ip> -p<port> [-i<tablet_id>]" << endl;
			cout << "Example: " << argv[0] << " -h127.0.0.1 -p20081 [-isd_server_res,,1208485730497260]" << endl;

			exit( EXIT_FAILURE );
		 }
		
		rootTablet.init( g_ip_address, g_port, 3600*1000, 3600*1000 );
		rootTablet.reconnect();
		if ( !rootTablet.is_connected() ) {
			fprintf( stderr, "connect to %s:%d fail: %d %s\n", g_ip_address.c_str(), g_port, errno, strerror( errno ) );		
			exit( EXIT_FAILURE );
		}

		g_root = &rootTablet;
	}

	XBaseServer tablet;
	if ( IS_TABLET_SHELL( g_shell_type ) ) {
		if ( g_ip_address.empty() || g_port == 0 || g_tablet_id.empty() ) {
			cout << "Usage: " << argv[0] << " -h<ip> -p<port> [-i<tablet_id>]" << endl;
			cout << "Example: " << argv[0] << " -h127.0.0.1 -p20081 [-isd_server_res,,1208485730497260]" << endl;

			exit( EXIT_FAILURE );
		}
			
		tablet.init( g_ip_address.c_str(), g_port, 3600*1000, 3600*1000 );
		tablet.reconnect();
		if ( !tablet.is_connected() ) {
			fprintf( stderr, "connect to %s:%d fail: %d %s\n", g_ip_address.c_str(), g_port, errno, strerror( errno ) );		
			exit( EXIT_FAILURE );
		}

		TabletInfo tabletInfo;
		std::vector< string > result;
		int count = split( result, g_tablet_id, ',' );
		if ( count != 3 ) {
			cout << "INVALID tablet id " << "\"" << g_tablet_id << "\"" << endl;
			exit( EXIT_FAILURE );
		}
		if ( count == 3 ) {
			tabletInfo._table_name = result[0];
			tabletInfo._start_row = result[1];
			tabletInfo._id = atoi( result[2].c_str() );
			tabletInfo._state = 1;
		} 
		tablet.tablet_info() = tabletInfo;
		g_tablet = &tablet;
	}

	if ( !cmdLine.empty() ) {
		run( cmdLine );
		exit( EXIT_SUCCESS );
	}

	if ( !scriptFile.empty() ) {
		ifstream in( scriptFile.c_str() );
		if ( in.fail() ) {
			cout << "open file " << scriptFile << " fail: " << strerror( errno ) << endl;
			exit( EXIT_FAILURE );
		}

		int taskCount = 0;
		string line;	
		while ( getline( in, line ) ) {
			if ( !line.empty() ) {
				++taskCount;
				cout << "*************************** " << taskCount << ". command ***************************" << endl;
				cout << "xbasesql> " << line << endl;
				run( line );
			}
		}

		exit( EXIT_SUCCESS );
	}

	//chdir("names");
	init_commands();
	rl_completion_entry_function = next_command_generator;
	rl_attempted_completion_function = fileman_completion;

	welcome();

	int exit_flag = 0;
	list< string > historyCmds;
		const char *home_path = ::getenv("HOME");
		if(home_path == NULL)
			home_path = "/tmp";
		string xbaseHistoryFilename = home_path;
		xbaseHistoryFilename += "/.xbase_history";
	loadHistoryCommand( xbaseHistoryFilename, historyCmds );
	while (true) {
		char* ptr = readline("xbasesql> ");		
		if ( ptr && *ptr ) {
			add_history(ptr);

			if ( historyCmds.size() >= 1000 ) {
				historyCmds.pop_front();
			}
			historyCmds.push_back( ptr );			
			writeHistoryCommand( xbaseHistoryFilename, historyCmds );
		}
		run( ptr, &exit_flag );
		free(ptr);

		if ( exit_flag ) {
			exit( EXIT_SUCCESS );
		}
	}
}

